<?php
/**
 * View
 *
 * VIEW 문서를 처리합니다.
 *
 * @package boot
 * @author  Platform Team
 */

class View
{
    const ERROR_INIT_FAIL       = 1;
    const ERROR_NOT_FOUND_PATH  = 2;

    /**
     * 인스턴스
     * @var View
     */
    private static $instance;

    /**
     * 해당 클래스를 구동 시킬 수 있는지 판별
     */
    private $isProgress = false;

    /**
     * 언어팩 객체
     * @deprecated
     * @var Language
     */
    private $lang = false;

    /**
     * 문서의 확장자
     */
    private $sExt;

    /**
     * 현재 루트 디렉토리
     * (VIEW 내부 문서에서 import 메소드를 활용할 경우를 위한 처리 변수)
     */
    private $sCurrentRoot;

    /**
     * 현재 리소스 루트 디렉토리
     * (VIEW 내부 문서에서 import 메소드를 활용할 경우를 위한 처리 변수)
     */
    private $sCurrentResRoot;

    /**
     * 현재 데이터
     * (VIEW 내부 문서에서 import 메소드를 활용할 경우를 위한 처리 변수)
     */
    private $aCurrentData;

    /**
     * 현재 컨트롤러 객체
     * @var object
     */
    private $oCurrentController;

    /**
     * 생성자
     */
    private function __construct() {}

    /**
     * singleton
     *
     * @return View
     */
    public static function getInstance()
    {
        if (!self::$instance) self::$instance = new self;

        return self::$instance;
    }

    /**
     * 인스턴스 반환
     *
     * @return View
     */
    public static function instance()
    {
        return self::$instance;
    }

    /**
     * 인스턴스 파괴
     */
    public static function instancedel()
    {
        self::$instance = null;
    }

    /**
     * 초기화 진행
     *
     * @param Language $oLang      언어팩 객체
     * @param string $sExt         View 문서 확장자
     * @return  boolean
     */
    public function initialize($oLang, $sExt=FW_VIEW_EXT)
    {
        if (!is_object($oLang)) {
            $sErrorMsg = 'View initialize failed';
            Exceptioner::run(FW_EXCEPTION_VIEW, $sErrorMsg, self::ERROR_INIT_FAIL);
            throw new Exception($sErrorMsg, E_ERROR);
        }

        $this->lang         = $oLang;
        $this->sExt         = $sExt;
        $this->isProgress   = true;

        return true;
    }

    /**
     * 현재 Display 되는 VIEW 문서의 루트 디렉토리를 설정
     *
     * @param string $sRoot
     * @return  string
     */
    private function currentRoot($sRoot)
    {
        $this->sCurrentRoot = $sRoot;
        return $this->sCurrentRoot;
    }

    /**
     * 현재 Resource 디렉토리의 루트 설정
     *
     * @param string $sResRoot
     * @return  string
     */
    private function currentResRoot($sResRoot)
    {
        $this->sCurrentResRoot = $sResRoot;
        return $this->sCurrentResRoot;
    }

    /**
     * 현재 데이터 설정
     *
     * @param array $aData
     * @return  array
     */
    private function currentData($aData, $aDataAdd=null)
    {
        $this->aCurrentData = false;

        $aDataAdd = is_array($aDataAdd) ? $aDataAdd : false;
        $aData = is_array($aData) ? $aData : false;

        if ($aData !== false) {
            $this->aCurrentData = $aData;
        }

        if ($aDataAdd !== false) {
            if ($this->aCurrentData !== false) {
                $this->aCurrentData = array_merge($this->aCurrentData, $aDataAdd);
            } else {
                $this->aCurrentData = $aDataAdd;
            }
        }

        return $this->aCurrentData;
    }

    /**
     * 현재 컨트롤러 객체 설정
     *
     * @param   $Controller
     * @return  object
     */
    private function currentController($Controller)
    {
        $this->oCurrentController = $Controller;
        return $this->oCurrentController;
    }

    /**
     * VIEW 문서 경로를 반환합니다.
     *
     * @param string $sPath
     * @param string $sRoot
     * @return  string
     */
    private function viewPath($sPath, $sRoot)
    {
        $sPath = $sRoot . '/' . $sPath . '.' . $this->sExt;
        return $sPath;
    }

    /**
     * IMPORT 루트 디렉토리 반환
     *
     * @return string
     */
    private function importRoot()
    {
        $sRoot = dirname($this->sCurrentRoot) . '/' . 'import';
        return $sRoot;
    }

    /**
     * Import
     *
     * @param string $sPath
     */
    private function import($sPath, $sRoot=null)
    {
        if (!$sRoot) {
            $isCurrentRootChange = true;
            $sRoot = $this->importRoot();
        } else {
            $isCurrentRootChange = false;
        }

        $this->display($sRoot, $this->sCurrentResRoot, $sPath, $this->aCurrentData, true, null, $this->oCurrentController, $isCurrentRootChange);
    }

    /**
     * Language
     *
     * @deprecated
     * @param string $sName
     * @return  string
     */
    private function lang($sName)
    {
        $sRoot = $this->sCurrentResRoot;
        return $this->lang->read($sRoot, $sName);
    }

    /**
     * Controller 객체 반환
     *
     * @return object | null
     */
    private function controller()
    {
        return $this->oCurrentController;
    }

    /**
     * VIEW 문서를 출력한다.
     *
     * @param string $sRoot    문서의 루트 디렉토리 지정
     * @param string $sResRoot 리소스 루트 디렉토리 지정
     * @param string $sPath    경로
     * @param array $aData    키=>값 형태의 데이터
     * @param boolean $isPrint  브라우져 기본 출력 여부 (기본 : true)
     * @param array $aDataAdd 추가 데이터
     * @return true | string
     */
    public function display($sRoot, $sResRoot, $sPath, $aData=null, $isPrint=true, $aDataAdd=null, $Controller=null, $isCurrentRootChange=true)
    {
        if ($this->isProgress === false) {
            $sErrorMsg = 'View initialize failed';
            Exceptioner::run(FW_EXCEPTION_VIEW, $sErrorMsg, self::ERROR_INIT_FAIL);
            throw new Exception($sErrorMsg, E_ERROR);
        }

        $this->currentController($Controller);

        $aData = $this->currentData($aData, $aDataAdd);

        if (is_array($aData)) {
            foreach ($aData as $k => $v) {
                $$k = $v;
            }
        }

        if ($isCurrentRootChange === true) {
            $sRoot = $this->currentRoot($sRoot);
        }

        $sResRoot = $this->currentResRoot($sResRoot);
        $sPath = $this->viewPath($sPath, $sRoot);

        if (!file_exists($sPath) || !file_exists($sResRoot)) {
            $sErrorMsg = 'view file is not found - ' . $sPath . ', ' . $sResRoot;
            Exceptioner::run(FW_EXCEPTION_VIEW, $sErrorMsg, self::ERROR_NOT_FOUND_PATH);
            throw new Exception($sErrorMsg, E_ERROR);
        }

        ob_start();

        if ((bool)ini_get('short_open_tag') === false) {
            echo eval('?>'.preg_replace("/;*\s*\?>/", "; ?>", str_replace('<?=', '<?php echo ', file_get_contents($sPath))));
        } else {
            include($sPath);
        }

        if ($isPrint === false) {
            $sOutput = ob_get_contents();
            ob_end_clean();
            return $sOutput;
        }

        ob_end_flush();

        return true;
    }
}
